
use crate::commands::container_fs::container_overlayfs;
use crate::utilty::{container_fs_dir, container_fs_dir_get, create_dir, log_error, log_info, record_pid_and_container_name};
use nix::fcntl::{open, OFlag};
use nix::libc::{__errno_location, c_int, chdir, clone, execvp, exit, malloc, mount, printf, rmdir, setenv, strerror, umount, umount2, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, MNT_DETACH, MS_NODEV, MS_NOEXEC, MS_NOSUID, MS_PRIVATE, MS_REC, SIGCHLD};
use nix::sys::stat::Mode;
use nix::unistd::{close, dup2, pivot_root};

use std::env::args;
use std::ffi::{c_void, CString};

use std::path::Path;
use std::ptr::{null, null_mut};
use crate::cgroup::cgroup::cgroup;
pub fn wrap_run(){
    if args().count()<5{
        log_error("args count error".to_string());
        return;
    }
    run()
}
//解压镜像
//创建overlayfs目录
//创建新namespace
//设置cgroup
///jdocker run <img_base_name> <container_name> <command...>
pub fn run() {
    unsafe {
        let mut child_pid = 0;
        //busybox.tar
        let image_name = args().nth(2).expect("no argument image");
        let mut container_name = image_name.clone();
        container_name.push_str("_");
        container_name.push_str(args().nth(3).expect("no argument container").as_str());
        {
            //创建overlayfs目录
            container_fs_dir(image_name,container_name.clone());
        }
        //创建namespace
        {
            let child_stack_space = 1024 * 1024 * 500;
            let child_stack = malloc(child_stack_space);
            let flags = CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS;
            child_pid = clone(
                child_function,
                child_stack.offset(child_stack_space as isize),
                flags | SIGCHLD,
                null_mut(),
            );
            if child_pid == -1 {
                log_error("create namespace failed".to_string());
                printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
                exit(*__errno_location());
            } else {
                log_info(format!("child pid: {}", child_pid));
            };
        }
        //设置cgroup
        {
            cgroup(1024 * 1024 * 200, "50000 100000", child_pid).expect("cgroup error");
        }
        //必须建立一种child_pid与容器名的映射关系
        //jdocker稍后可以查看哪个镜像的哪个容器的一号进程的pid是多少
        //容器名 = "<镜像名>_<容器名>"
        record_pid_and_container_name(child_pid,container_name);
    }
}
extern "C" fn child_function(_: *mut c_void) -> c_int {
    unsafe {
        let image_name = args().nth(2).expect("no argument image");
        let mut container_name = image_name.clone();
        container_name.push_str("_");
        container_name.push_str(args().nth(3).expect("no argument container").as_str());
        let dirs = container_fs_dir_get(image_name,container_name);
        println!("{:?}", dirs);
        //消除peer_group share关系
        mount(null(), c"/".as_ptr(), null(), MS_REC | MS_PRIVATE, null());
        //mount overlayfs文件系统
        container_overlayfs(
            dirs.image_dir.clone(),
            dirs.container_dir.clone(),
            dirs.worker_dir.clone(),
            dirs.merge_dir.clone(),
        );
        //指定new_root和old_root
        let new_root = Path::new(dirs.merge_dir.as_str());
        let old_root = new_root.join(".old_root");
        create_dir(old_root.display().to_string());
        //切换mnt命令空间根目录
        pivot_root(new_root, &old_root).expect("pivot_root failed");
        //切换进程根目录
        chdir(c"/".as_ptr());
        //umount old_root
        let c_string_old_root = CString::new("/.old_root").expect("CString failed");
        if umount2(c_string_old_root.as_ptr(), MNT_DETACH)==-1 {
            log_error("umount failed".to_string());
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            exit(*__errno_location());
        };
        //remove old_root_dir
        if rmdir(c"/.old_root".as_ptr())==-1 {
            log_error("rmdir failed".to_string());
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            exit(*__errno_location());
        };
        //mount proc 文件系统
        mount(
            null(),
            c"/proc".as_ptr(),
            c"proc".as_ptr(),
            MS_NOEXEC | MS_NOSUID | MS_NODEV,
            null(),
        );
        //设置环境变量
        setenv(c"PATH".as_ptr(), c"/bin".as_ptr(), 1);
        //关闭标准输出
        close(1).expect("close fd 1 failed");
        //打开log文件作为标准输出
        let log_fd = open(
            "/.log",
            OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC,
            Mode::S_IRWXU | Mode::S_IRWXO | Mode::S_IRWXG,
        )
        .expect("create log file failed");
        //关闭标准输出错误 并使log文件作为标准输出错误
        dup2(log_fd, 2).expect("dup2 log file failed");
        //关闭标准输入
        close(0).expect("close fd 0 failed");
        //执行用户命令
        let exec = args()
            .into_iter()
            .skip(4)
            .map(|arg| CString::new(arg.as_str()).unwrap())
            .collect::<Vec<_>>();
        let exec = exec.iter().map(|e| e.as_c_str()).collect::<Vec<_>>();
        log_info(format!("child exec: {:?}", exec));
        let exec = exec
            .iter()
            .map(|e| e.as_ptr())
            .chain(Some(null()))
            .collect::<Vec<_>>();
        execvp(exec[0], exec.as_ptr());
        printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
        1
    }
}
